using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
using Microsoft.Synchronization;
using Microsoft.WindowsAzure.StorageClient;
using ThreadState = System.Threading.ThreadState;

namespace Microsoft.WindowsAzure.Accelerator.Synchronization
{
    /// <summary>
    /// Manages the synchronization providers for Azure cloud synchronization.
    /// </summary>
    public class SyncManager
    {
        private ISyncProvider syncProvider;
        private Thread syncThread;

        /// <summary>
        /// Gets or sets the account.
        /// </summary>
        /// <value>The account.</value>
        public CloudStorageAccount Account
        {
            get { return syncProvider.Account;}
            set { syncProvider.Account = value;  }
        }

        /// <summary>
        /// Gets or sets the name of the container.
        /// </summary>
        /// <value>The name of the container.</value>
        public String ContainerName
        {
            get { return syncProvider.ContainerName; }
            set { syncProvider.ContainerName = value; }
        }

        /// <summary>
        /// Gets or sets the local path.
        /// </summary>
        /// <value>The local path.</value>
        public String LocalPath
        {
            get { return syncProvider.LocalPath; }
            set { syncProvider.LocalPath = value; }
        }

        /// <summary>
        /// Gets or sets the sync direction.
        /// </summary>
        /// <value>The sync direction.</value>
        public SyncDirectionOrder SyncDirection
        {
            get { return syncProvider.SyncDirection; }
            set { syncProvider.SyncDirection = value; }
        }
        
        /// <summary>
        /// Gets or sets the interval.
        /// </summary>
        /// <value>The interval.</value>
        public TimeSpan Interval
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is started.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is started; otherwise, <c>false</c>.
        /// </value>
        public Boolean IsStarted
        {
            get; private set;
        }

        /// <summary>
        /// Validates the configuration.
        /// </summary>
        private void ValidateConfiguration()
        {
            if (String.IsNullOrEmpty(LocalPath))
                throw new ArgumentException("SyncManager | A valid local path is required.");
            if (String.IsNullOrEmpty(ContainerName))
                throw new ArgumentException("SyncManager | A valid blob storage container name is required.");
            if (Account == null)
                throw new ArgumentException("SyncManager | A valid cloud storage account is required.");
            var r = Account.CreateCloudBlobClient().GetContainerReference(ContainerName);
            if (!r.Exists())
            {
                r.CreateIfNotExist();
            }
            if (!Directory.Exists(LocalPath))
            {
                Directory.CreateDirectory(LocalPath);
                if (!Directory.Exists(LocalPath))
                    throw new ArgumentException("SyncManager | Unable to create local directory.");
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SyncManager"/> class.
        /// </summary>
        /// <param name="providerType">Type of the provider.</param>
        public SyncManager(SyncProviderType providerType)
        {
            syncProvider = LoadProvider(providerType);
            IsStarted = false;
            Interval = TimeSpan.Zero;
        }

        /// <summary>
        /// Factory method which create the provider.
        /// </summary>
        /// <param name="providerType">Type of the provider.</param>
        /// <returns></returns>
        private static ISyncProvider LoadProvider(SyncProviderType providerType)
        {
            switch (providerType)
            {
                case SyncProviderType.BlobSync:  return new BlobSync();
                case SyncProviderType.CloudSync: return new CloudSync();
                default:                         return new CloudSync();
            }
        }
        
        /// <summary>
        /// Start the thread to sync blobs at intervals until Stop() is called.
        /// </summary>
        public void Start()
        {
            syncThread = new Thread(() =>
            {
                while (true)
                {
                    Sync();
                    if (Interval == TimeSpan.Zero)
                        break;
                    Thread.Sleep(Interval);
                }
            });
            syncThread.Start();
        }

        /// <summary>
        /// Stops the blob sync, terminating any in-progress transfers.
        /// </summary>
        public void Stop()
        {
            if (syncThread == null) return;
            lock (syncThread)
            {
                if (IsStarted && syncThread.ThreadState == ThreadState.Running)
                    syncThread.Abort();
            }
            IsStarted = false;
        }

        /// <summary>
        /// Performs the synchronization of file system and blob storage.
        /// </summary>
        public void Sync()
        {
            ValidateConfiguration();
            IsStarted = true;
            try
            {
                syncProvider.StartSync();
            }
            catch (Exception ex)
            {
                Trace.TraceError("SyncManager| An unrecoverable error occured:  {0}. {1}", ex.Message, ex);
            }
            IsStarted = false;
        }
    }
}